package com.m3958.firstgwt.server.command;

import java.io.IOException;
import java.util.List;

import javax.persistence.Query;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import com.google.inject.Inject;
import com.google.inject.persist.Transactional;
import com.m3958.firstgwt.client.datasource.AsyncTaskDataSource;
import com.m3958.firstgwt.client.types.SmartSubOperationName;
import com.m3958.firstgwt.server.dao.BaseDao;
import com.m3958.firstgwt.server.dao.WFChangeLogDao;
import com.m3958.firstgwt.server.exception.SmartJpaException;
import com.m3958.firstgwt.server.model.AsyncTask;
import com.m3958.firstgwt.server.model.BaseModel;
import com.m3958.firstgwt.server.model.WebSite;
import com.m3958.firstgwt.server.model.WorkFlowProject;
import com.m3958.firstgwt.server.response.ErrorMessageResponse;
import com.m3958.firstgwt.server.service.RequestScopeObjectService;
import com.m3958.firstgwt.server.service.SiteConfigService;
import com.m3958.firstgwt.server.session.SessionUser;
import com.m3958.firstgwt.server.types.HasToJson;
import com.m3958.firstgwt.shared.type.SmartParameterName;



public class FetchCommand extends BaseCommand implements Command{
	
//	@Inject
//	private ClojureService clojureService;
	
	private boolean writeResponse = true;
	
	@Inject
	private SiteConfigService msc;
	
	private JSONObject result = null;
	
	public void execute() throws IOException{
		
		// if hit cache,return cache;
		SmartSubOperationName sson = SmartSubOperationName.NO_SUB_OPERATION;
		if(reqPs.getSubOpType() != null)
			sson = SmartSubOperationName.valueOf(reqPs.getSubOpType());
		
		switch (sson) {
		case FETCH_ONE_TO_MANY:
			result = smartOneToManyFetch();
			break;
		case FETCH_CHILDREN:
			result = smartFetchChildren();
			break;
		case MANY_TO_MANY:
			result = smartManyToManyFetch();
			break;
		case FETCH_TWO_JOIN:
			result = smartFetchTwoJoin();
			break;
		case CUSTOM_FETCH:
			result = smartCustomFetch();
			break;
		case FETCH_NO_RELATION:
			result = smartNoRelationFetch();
			break;
		case FETCH_ONE:
			result = smartFetchOne();
			break;
		case NAMED_QUERY:
			result = smartNamedQueryFetch();
			break;
		case HAS_PERMISSION:
			result = hasPermission();
			break;
		case FETCH_SHARED_USERS:
			result = fetchSharedUsers();
			break;
		case FETCH_SHARED_GROUPS:
			result = fetchSharedGroups();
			break;
		case NOT_CREATOR_HAS_PERMISSION:
			result = fetchNotCreatorHasPermission();
			break;
		case NOT_CREATOR_HAS_GPERMISSION:
			result = fetchNotCreatorHasGPermission();
			break;
		case FETCH_DESCENDENT_CONTENT:
			result = fetchChildrenArticles();
			break;
		case FETCH_HAS_USER_PERMISSION:
			result = fetchHasUserPermissions();
			break;
		case FETCH_HAS_USER_ROLE:
			result = fetchHasUserRoles();
			break;
		case JPQL:
			result = fetchJpQl();
			break;
		case FETCH_TOP:
			result = fetchTop();
			break;
		default:
			result = smartFetch();
		}
		
		if(writeResponse){
			JSONObject m3985jo = new JSONObject();
			m3985jo.element("tid", reqPs.getTid());
			if(!errors.isEmpty()){
				result = injector.getInstance(ErrorMessageResponse.class).getJsonObject();				
			}
			
			result.getJSONObject("response").element("m3958extra", m3985jo);
			if(reqPs.isJsonp()){
				autils.writeJsonpResponse(res, result.toString(), reqPs.getStringValue("callback"));
			}else{
				if(reqPs.isIframe()){
					irss.addIframeResult(reqPs.getTid(), result.toString());
					autils.writeHtmlResponseWithTemplate(res, "", "");
				}else{
					autils.writeJsonResponse(res,result.toString());
				}
			}
		}
	}
	
	private JSONObject fetchTop() {
		List<BaseModel> results = getDao().smartFetchTop();
		Integer totalRows = getDao().smartFetchTopCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject fetchJpQl() {
		String qs = reqPs.getStringValue("tq","");
		if(qs.isEmpty())return null;
		Query q = emp.get().createQuery(qs);
		q.setMaxResults(1000);
		List<Object[]> rs = q.getResultList();
		return autils.getObjectListResponse(rs, 0, rs.size());
	}

	private JSONObject fetchHasUserRoles() {
		List<BaseModel> results = getDao().smartHasUserRoleFetch();
		Integer totalRows = getDao().smartHasUserRoleFetchCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject fetchHasUserPermissions() {
		List<BaseModel> results = getDao().smartHasUserPermissionFetch();
		Integer totalRows = getDao().smartHasUserPermissionFetchCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject fetchChildrenArticles() {
		List<BaseModel> results = getDao().getDescendantContent();
		Integer totalRows = getDao().getDescendantContentCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject fetchNotCreatorHasPermission() {
		BaseDao bd = getDao();
		List<BaseModel> results = bd.fetchNotCreatorHasPermission();
		Integer totalRows = bd.fetchNotCreatorHasPermissionCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	
	private JSONObject fetchNotCreatorHasGPermission() {
		List<BaseModel> results = getDao().fetchNotCreatorHasGPermission();
		Integer totalRows = getDao().fetchNotCreatorHasGPermissionCount();
		return autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}


	private JSONObject fetchSharedUsers() {
		List<HasToJson> results = getDao().fetchSharedUsers();
		Integer totalRows = getDao().fetchSharedUsersCount();
		return  autils.getJsonResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	
	private JSONObject fetchSharedGroups() {
		List<HasToJson> results = getDao().fetchSharedGroups();
		Integer totalRows = getDao().fetchSharedGroupsCount();
		return  autils.getJsonResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject hasPermission() {
		List<BaseModel> results = getDao().smartHasPermissionFetch();
		Integer totalRows = getDao().smartHasPermissionFetchCount();
		results = processExtraJsonFields(results);
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private List<BaseModel> processExtraJsonFields(List<BaseModel> results) {
		if(results.size() == 0)return results;
		if(results.get(0) instanceof WebSite && "vv".equals(reqPs.getJsonv())){
			SessionUser su = injector.getInstance(SessionUser.class);
			if(su.isLogined()){
				RequestScopeObjectService rso = injector.getInstance(RequestScopeObjectService.class);
				for(BaseModel bm : results){
					WebSite ws = (WebSite) bm;
					ws.setSiteEditor(rso.isSiteOwner(ws));
					ws.setSiteOwner(rso.isSiteOwner(ws));
				}
			}
		}
		return results;
	}

	private JSONObject smartNamedQueryFetch() {
		List<BaseModel> results = getDao().smartNamedQueryFetch();
		Integer totalRows = getDao().smartNamedQueryFetchCount();
		if("com.m3958.firstgwt.server.model.WorkFlowProject".equals(reqPs.getModelName())){
			res.setHeader("Cache-Control","no-cache, must-revalidate");
			String qn = reqPs.getStringValue(SmartParameterName.NAMED_QUERY_NAME);
			if(WorkFlowProject.NamedQueries.FIND_BY_USER_ID.equals(qn)){
				JSONObject jo = autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
				WFChangeLogDao wfclDao = injector.getInstance(WFChangeLogDao.class);
				jo.getJSONObject("response").element("lastTransId", wfclDao.getUserLastTransLogId());
				return jo;
			}else if(WorkFlowProject.NamedQueries.FIND_PROJECTS_BY_UUID.equals(qn)){
				JSONObject jo = autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
				WFChangeLogDao wfclDao = injector.getInstance(WFChangeLogDao.class);
				jo.getJSONObject("response").element("lastTransId", wfclDao.getItemLastTransLogId());
				return jo;
			}
		}
		
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject smartFetchOne() {
		BaseModel bm = getDao().find(reqPs.getModelId());
		return  autils.getListResponse(bm,"vvv");
	}

	private JSONObject  smartNoRelationFetch() {
		List<BaseModel> results = getDao().smartNoRelationFetch();
		Integer totalRows = getDao().smartNoRelationRowCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	private JSONObject smartCustomFetch(){
		List<BaseModel> results = getDao().smartCustomFetch();
		Integer totalRows = getDao().smartCustomCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	
	private JSONObject smartFetchTwoJoin() {
		List<BaseModel> results = getDao().smartTwoJoinFetch();
		Integer totalRows = getDao().smartTwoJoinRowCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	
	

	private JSONObject smartOneToManyFetch() {
		List<BaseModel> results = getDao().smartOneToManyFetch();
		Integer totalRows = getDao().smartOneToManyCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	


	protected JSONObject smartFetch(){
//		if(msc.isCache_enable() && Lgb.class.getName().equals(reqPs.getModelName())){
//			String key = reqPs.getStringValue(LgbField.DEPARTMENT_IDS.getValue());
//			String v = memcached.getLgbs(key,req.getQueryString());
//			if(v == null){
//				List<BaseModel> results = getDao().smartFetch();
//				Integer totalRows = getDao().smartFetchCount();
//				v = autils.getListResponse(results,reqPs.getStartRow(),totalRows);
//				memcached.setLgbs(key,req.getQueryString(),v);
//			}
//			return  v;
//		}else{
//			if("com.m3958.firstgwt.server.model.FreeRecord".equals(reqPs.getModelName())){
//				return clojureFetch();
//			}
			List<BaseModel> results = getDao().smartFetch();
			Integer totalRows = getDao().smartFetchCount();
			if(AsyncTaskDataSource.className.equals(reqPs.getModelName())){
				doAfterFetch(results);
			}
			
			return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
//		}
	}

	@Transactional
	protected void doAfterFetch(List<BaseModel> results) {
		for(BaseModel bm : results){
			AsyncTask at = (AsyncTask) bm;
			if("SUCCESS".equals(at.getStatus()) || "FAILURE".equals(at.getStatus())){
				at.setNotified(true);
				emp.get().merge(at);
			}
		}
	}

	private JSONObject  smartFetchChildren() {
		List<BaseModel> results = getDao().smartFetchChildren();
		Integer totalRows = getDao().smartFetchChildrenCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}
	
	private JSONObject smartManyToManyFetch() {
		List<BaseModel> results = getDao().smartManyToManyFetch();
		Integer totalRows = getDao().smartManyToManyRowCount();
		return  autils.getListResponse(results,reqPs.getStartRow(),totalRows,reqPs.getJsonv());
	}

	public String getResult() {
		return result.toString();
	}
	
	@Override
	public void execute(boolean writeResponse) throws SmartJpaException,
			IOException {
		this.writeResponse = writeResponse;
		execute();
	}
	
	
	//clojure start
	
//	private JSONObject clojureFetch() {
//		TableSchema ts = emp.get().find(TableSchema.class, reqPs.getTableschemaid());
//		if(ts == null){
//			errors.addError(new SmartError("必须提供表单结构Id号！", 0));
//			return null;
//		}
//		return JSONObject.fromObject(clojureService.getFetchResult(req,ts));
//	}

	private JSONObject getListResponse(List<? extends BaseModel> results,Integer startRow,Integer totalRows,String jsonv){
		JSONArray dataField = new JSONArray();
		JSONObject responseField = new JSONObject().element("status", 0);
		if(results != null){
			responseField.element("startRow", startRow);
			responseField.element("endRow",startRow + results.size());
			responseField.element("totalRows", totalRows);
			for(BaseModel bm : results){
				dataField.add(bm.toJson(jsonv));
			}
		}else{
			responseField.element("startRow", 0);
			responseField.element("endRow",0);
			responseField.element("totalRows", 0);
		}
		
		responseField.element("data", dataField);
		
		JSONObject root = new JSONObject();
		root.element("response", responseField);
		
		return root;
	}
	

}
